package token

import (
	"fmt"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

//JwtCustomClaims token结构
type JwtCustomClaims struct {
	jwt.StandardClaims // 包中自带的默认属性
	Role               string
	UID                string `json:"uid"` // 当前访问人uid 自定义添加一些自己需要的元素
}

var skey = "iot base e&c"

//GenerateToken 生成token串
// 这里的参数一般为自己自定义的一些参数
func GenerateToken(userID string, role string, expire int) (string, error) {
	// 这里使用的是自定义结构体的方式床架claims，也可以使用jwt.MapClaims结构体
	/*
		claims := jwt.MapClaims{} // 其实就是个map
		claims["uid"] = userId
	*/

	claims := JwtCustomClaims{
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: int64(time.Now().Add(time.Hour * time.Duration(expire)).Unix()),
			//Issuer:    config.GetConfig().Encrypt.EncryptIssuer,
		},
		Role: role,
		UID:  userID,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 这里需要注意当使用jwt.SigningMethodHS256方式生成token串时，SignedString方法的参数应该是[]byte数组，其他方式也对key有着要求
	tokenStr, err := token.SignedString([]byte(skey))
	if err != nil {
		logrus.Errorf("[GenerateToken] generate token error,err_msg = %s", err.Error())
		return "", err
	}

	return tokenStr, nil
}

//ParseToken 解析token串
func ParseToken(tokenStr string) (cliams jwt.MapClaims, err error) {
	// 创建对象，直接调用parse方法
	var token *jwt.Token
	token, err = jwt.Parse(tokenStr, func(*jwt.Token) (interface{}, error) {
		// 注意这里也是[]byte数组
		return []byte(skey), nil
	})
	if err != nil {
		fmt.Println("[ParseToken] parse token error,err_msg = ", err.Error())
		return
	}

	// 获取jwt.Token对象后，获取定义的claims
	var ok bool
	// 注意 生成token串时无论是用自定义结构体的方式还是直接使用jwt.MapClaims，这里断言结果都为jwt.MapClaims（断言为自定义的结构体会失败）
	// 断言获取到的jwt.MapClaims实际上为map[string]interface{},使用key获取值时，需要再做一次类型断言，需要注意类型的转换，数值类型会被转化为float64（设置map["uid"] = 10,获取到的map["uid"]实际为float64类型的10.00000）
	cliams, ok = token.Claims.(jwt.MapClaims)
	if !ok {
		//logrus.Errorf("[ParseToken]token claims type error,can't convert")
		return cliams, fmt.Errorf("token claims can't convert to JwtCustomerClaims")
	}

	return cliams, nil
}

//const tokenErr = -340003

//AuthMiddleware 中间件
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		url := c.Request.URL.String()
		if url == "/api/su/login" {
			c.Next()
			return
		}
		tokenStr := c.Request.Header.Get("api_token")
		if tokenStr == "" {
			cookie, err := c.Request.Cookie("api_token")
			if err == nil {
				tokenStr = cookie.Value
			} else {
				fmt.Println("*********************", err)
			}
		}
		if tokenStr == "" {
			c.JSON(http.StatusUnauthorized, gin.H{})
			c.Abort()
			fmt.Println("*********************auth token abort****************")
			return
		}
		// 解析token
		claims, err := ParseToken(tokenStr)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{})
			fmt.Println("*********************auth token abort***********wwww*****", err)
			c.JSON(http.StatusUnauthorized, gin.H{})
			c.Abort()
			return
		}
		// 注意这里数值类型断言后的类型位float64
		//c.Set("uid", int(claims["uid"].(float64)))
		role := claims["Role"]
		if role != nil {
			srole := role.(string)
			c.Set("Role", srole)
		}
		uid := claims["UID"]
		if uid != nil {
			suid := uid.(string)
			c.Set("uid", suid)
		}

		c.Next()
	}
}
